﻿using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
namespace AiMoCore
{
    partial class GHCSharpScriptInstance
    {
        IGH_DataAccess DA;
        string[] m_Outputs;
        string[] m_Inputs;
        internal void SetDataAccess(IGH_DataAccess access, string[] inputs, string[] outputs)
        {
            DA = access;
            m_Inputs = inputs;
            m_Outputs = outputs;
        }

        int IndexOfParam(string[] m_Params, string paramName)
        {
            for (int i = 0; i < m_Params.Length; i++)
            {
                if (m_Params[i] == paramName)
                {
                    return i;
                }
            }
            return -1;
        }

        public bool GetList<T>(string paramName, out T[] valueArray)
        {
            int index = IndexOfParam(m_Inputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in GetList(). You must use GetList(\"{paramName}\", {typeof(T).Name}[] valueArray) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            List<T> list = new List<T>();
            bool re = false;

            switch (typeof(T).FullName)
            {
                case "Rhino.Geometry.Polyline":
                    List<Curve> crvs = new List<Curve>();
                    re = DA.GetDataList(index, crvs);
                    if (re)
                    {
                        foreach (Curve crv in crvs)
                        {
                            list.Add(ToPolyline<T>(crv));
                        }
                        re = list.Count > 0;
                    }
                    break;
                case "Rhino.Geometry.BoundingBox":
                    List<Box> boxes = new List<Box>();
                    re = DA.GetDataList(index, boxes);
                    if (re)
                    {
                        foreach (Box box in boxes)
                        {
                            list.Add(ToBoundingBox<T>(box));
                        }
                        re = list.Count > 0;
                    }
                    break;
                default:
                    re = DA.GetDataList(index, list);
                    break;
            }
            valueArray = list.ToArray();
            return re;
        }

        public bool GetItem<T>(string paramName, out T value)
        {
            int index = IndexOfParam(m_Inputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in GetItem(). You must use GetItem(\"{paramName}\", out {typeof(T).Name} value) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            bool re = false;
            switch (typeof(T).FullName)
            {
                case "Rhino.Geometry.Polyline":
                    Curve crv = null;
                    re = DA.GetData(index, ref crv);
                    if (re) value = ToPolyline<T>(crv);
                    else value = default;
                    return re;
                case "Rhino.Geometry.BoundingBox":
                    Box box = default;
                    re = DA.GetData(index, ref box);
                    if (re) value = ToBoundingBox<T>(box);
                    else value = default;
                    return re;
                default:
                    value = default;
                    return DA.GetData(index, ref value);
            }

        }
        public bool GetTree<T>(string paramName, out DataTree<T> valueTree)
        {
            int index = IndexOfParam(m_Inputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in GetTree(). You must use GetTree(\"{paramName}\", out DataTree<{typeof(T).Name}> valueTree) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            bool re = DA.GetDataTree(index, out GH_Structure<IGH_Goo> tree);
            if (re)
            {
                valueTree = new DataTree<T>();
                for (int i = 0; i < tree.PathCount; i++)
                {
                    List<IGH_Goo> gooBranch = tree.Branches[i];
                    List<T> valueList = new List<T>();
                    foreach (IGH_Goo goo in gooBranch)
                    {
                        if (goo.CastTo(out T resultItem))
                        {
                            valueList.Add(resultItem);
                        }
                        else
                        {
                            throw new Exception($"Cannot auto cast IGH_Goo data {goo} to type <{typeof(T).Name}>, please use GetTree(out DataTree<IGH_Goo> valuesTree) then convert to type <{typeof(T).Name}> manually.");
                        }
                    }
                    valueTree.AddRange(valueList, tree.Paths[i]);
                }
            }
            else
            {
                valueTree = null;
            }
            return re;
        }
        T ToPolyline<T>(Curve crv)
        {
            if (crv == null) return default;
            if (!crv.TryGetPolyline(out Polyline polyline))
            {
                throw new Exception($"Cannot cast Curve to Polyline");
            }
            if (polyline is T t) return t;
            return default;
        }
        T ToBoundingBox<T>(Box box)
        {
            if (box.BoundingBox is T t)
                return t;
            return default;
        }

        public bool SetList<T>(string paramName, IEnumerable<T> valueArray)
        {
            int index = IndexOfParam(m_Outputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in SetList(). You must use SetList(\"{paramName}\", IEnumerable<{typeof(T).Name}> valueArray) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            return DA.SetDataList(index, valueArray);
        }
        public bool SetItem<T>(string paramName, T value)
        {
            int index = IndexOfParam(m_Outputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in SetItem(). You must use SetList(\"{paramName}\", {typeof(T).Name} value) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            return DA.SetData(index, value);
        }
        public bool SetTree<T>(string paramName, DataTree<T> valueTree)
        {
            int index = IndexOfParam(m_Outputs, paramName);
            if (index < 0)
            {
                throw new Exception($"Cannot Get Param {paramName} in SetTree(). You must use SetList(\"{paramName}\", DataTree<{typeof(T).Name}> valueTree) in SolveInstance Method and input paramName \"{paramName}\" must be a const string during compilation.");
            }
            return DA.SetDataTree(index, valueTree);
        }
    }

}
